package com.scs.application.core.serial;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.core.exceptions.BusinessException;
import com.scs.application.core.utils.CacheUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.modules.upms.entity.SerialOwner;
import com.scs.application.modules.upms.service.SerialOwnerService;
import com.scs.application.modules.upms.service.SerialRuleService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;

import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description: 编号规则生成，基于redis，现已废弃 {@see fortec.common.feign.utils.SerialUtils}
 */
@Slf4j
public class SerialUtils implements InitializingBean {

    @Autowired
    private SerialRuleService serialRuleService;

    private static SerialOwnerService serialOwnerService;

    @Autowired
    private SerialOwnerService serialOwnerService1;

    @Override
    public void afterPropertiesSet() {
        serialRuleService.syncToCache();
        this.serialOwnerService = serialOwnerService1;
    }

    // 序列号列表  缓存键值
    private static final String CACHE_LIST_PREFIX = "serialList";

    // 序列号  当前序列前缀
    private static final String CACHE_SEQ_PREFIX = "serial:seq:";

    private static final String DEFAULT_OWNER = "0";


    private static final String DATETIME_KEY = "datetime_";
    private static final String SEQUENCE_KEY = "sequence_";
    private static final String STRING_KEY = "string_";

    public static boolean addSerialRule(SerialRule rule) {
        if (rule == null || StringUtils.isBlank(rule.getRuleCode())) {
            return false;
        }
        CacheUtils.put(CACHE_LIST_PREFIX, rule.getRuleCode(), rule);
        return true;
    }

    public static SerialRule getSerialRule(String ruleCode) {
        return (SerialRule) CacheUtils.get(CACHE_LIST_PREFIX, ruleCode);
    }

    public static List<SerialRule> list() {
        List<SerialRule> serialRules = Lists.newArrayList();
        List<String> keys = CacheUtils.getCacheKeys(CACHE_LIST_PREFIX);
        for(String key : keys) {
            serialRules.add((SerialRule)CacheUtils.get(CACHE_LIST_PREFIX, key));
        }
        return serialRules;
    }


    public static boolean delete(String ruleCode) {
        CacheUtils.remove(CACHE_LIST_PREFIX, ruleCode);
        return true;
    }

    /**
     * 依据定义的编号规则进行生成
     *
     * @param ruleCode
     * @return
     */
    public static String generateCode(String ruleCode) {
        List<String> codes = batchGenerateCode(ruleCode, DEFAULT_OWNER, 1);
        return codes.isEmpty() ? null : codes.get(0);
    }


    public static String generateCode(String ruleCode, String owner) {
        List<String> codes = batchGenerateCode(ruleCode, owner, 1);
        return codes.isEmpty() ? null : codes.get(0);
    }

    public static List<String> batchGenerateCode(String ruleCode, int count) {
        return batchGenerateCode(ruleCode, DEFAULT_OWNER, count);
    }

    public static List<String> batchGenerateCode(String ruleCode, String owner, int count) {
        String lockKey = MessageFormat.format("serial:lock:{0}:{1}", owner, ruleCode);
        synchronized (ruleCode.intern()) {
            List<String> codes = Lists.newArrayList();

            try {
                SerialRule serialRule = SerialUtils.getSerialRule(ruleCode);

                if (serialRule == null) {
                    throw new BusinessException("编号规则[" + ruleCode + "]未找到，请确认是否配置");
                }

                SerialOwner serialOwner = serialOwnerService.getOne(
                        Wrappers.<SerialOwner>query()
                                .eq("rule_id", serialRule.getId())
                                .eq("target", owner)
                                .last("limit 1"));
                String currentCode = StringUtils.defaultString(serialOwner == null ? null : String.valueOf(serialOwner.getCurrentCode()));

                if (serialOwner == null) {
                    serialOwner = new SerialOwner();
                    serialOwner.setTarget(owner);
                    serialOwner.setRuleId(serialRule.getId());
                }
                int partNum = serialRule.getPartNum();
                String newCode = "";

                String delimiter = StringUtils.defaultString(serialRule.getDelimiter());

                String[] parts = new String[]{serialRule.getPart1(), StringUtils.defaultString(serialRule.getPart2()),
                        StringUtils.defaultString(serialRule.getPart3()), StringUtils.defaultString(serialRule.getPart4())};
                String[] partConds = new String[]{serialRule.getPart1Cond(),
                        StringUtils.defaultString(serialRule.getPart2Cond()),
                        StringUtils.defaultString(serialRule.getPart3Cond()),
                        StringUtils.defaultString(serialRule.getPart4Cond())};

                int totalLength = 0;
                for (int index = 0; index < partNum; index++) {
                    String partCond = partConds[index];
                    if (partCond.startsWith(STRING_KEY)) {
                        totalLength += parts[index].length();
                    } else if (partCond.startsWith(DATETIME_KEY)) {
                        totalLength += Integer.parseInt(partConds[index].substring(DATETIME_KEY.length()));
                    } else if (partCond.startsWith(SEQUENCE_KEY)) {
                        totalLength += Integer.parseInt(partConds[index].substring(SEQUENCE_KEY.length()));
                    }
                    if (index != partNum - 1) {
                        totalLength += StringUtils.isBlank(delimiter) ? 0 : 1;
                    }
                }
                Date now = new Date();

                if (StringUtils.isNotBlank(currentCode)) {
                    if (totalLength != currentCode.length()) {
                        log.error("编号规则[{}]当前编码长度：{}，预期编码长度：{}，生成编码失败" , serialRule.getRuleCode(), currentCode.length(),
                                totalLength);
                        throw new RuntimeException("编号规则[" + serialRule.getRuleCode() + "]可能在使用之后规则重新变更，请联系管理员");
                    }
                }

                for (int i = 0; i < count; i++) {
                    newCode = "";
                    if (StringUtils.isBlank(currentCode)) {
                        for (int index = 0; index < partNum; index++) {
                            String partCond = partConds[index];
                            String partValue = parts[index];
                            if (partCond.startsWith(STRING_KEY)) {
                                newCode += partValue;
                            } else if (partCond.startsWith(DATETIME_KEY)) {
                                newCode += getSdfByPartCond(partConds[index]).format(now);
                            } else if (partCond.startsWith(SEQUENCE_KEY)) {
                                int partLength = Integer.parseInt(partConds[index].substring(SEQUENCE_KEY.length()));

                                // 序列初始值
                                if (StringUtils.isNotBlank(partValue)) {
                                    newCode += StringUtils.repeat("0" , (partLength - (partValue.length()))) + partValue;
                                } else {
                                    newCode += StringUtils.repeat("0" , (partLength - 1)) + "1";
                                }
                            } else {
                                throw new RuntimeException("编号规则[" + serialRule.getRuleCode() + "]配置异常，请联系管理员");
                            }
                            if (index != partNum - 1) {
                                newCode += delimiter;
                            }
                        }
                        if (StringUtils.isBlank(newCode)) {
                            throw new RuntimeException("编号规则[" + serialRule.getRuleCode() + "]  编码生成失败，请联系管理员");
                        }
                        currentCode = newCode;
                        codes.add(currentCode);
                        continue;
                    }

                    String[] partValues = new String[partNum];

                    /**
                     * 对当前编码进行解析，存入数组
                     */
                    if (StringUtils.isBlank(delimiter)) {
                        int beginIndex = 0;
                        int endIndex = 0;
                        for (int j = 0; j < partNum; j++) {
                            String partCond = partConds[j];
                            if (partCond.startsWith(STRING_KEY)) {
                                partValues[j] = parts[j];
                                beginIndex += partValues[j].length();
                                endIndex += partValues[j].length();
                                continue;
                            }
                            if (partCond.startsWith(DATETIME_KEY)) {
                                endIndex += Integer.parseInt(partCond.substring(DATETIME_KEY.length()));
                            } else if (partCond.startsWith(SEQUENCE_KEY)) {
                                endIndex += Integer.parseInt(partCond.substring(SEQUENCE_KEY.length()));
                            }
                            partValues[j] = currentCode.substring(beginIndex, endIndex);
                            beginIndex += partValues[j].length();
                        }
                    } else {
                        partValues = StringUtils.split(currentCode, delimiter);
                    }

                    boolean dateChanged = false;
                    /**
                     * 判断时间部分是否改变
                     */
                    for (int index = 0; index < partNum; index++) {
                        String partCond = partConds[index];
                        if (partCond.startsWith(DATETIME_KEY)) {
                            SimpleDateFormat sf = getSdfByPartCond(partCond);
                            if (!sf.format(now).equals(partValues[index])) {
                                dateChanged = true;
                                break;
                            }
                        }
                    }

                    for (int index = 0; index < partNum; index++) {
                        String part = parts[index];
                        String partCond = partConds[index];
                        String partValue = "";
                        if (StringUtils.isBlank(partCond)) {
                            throw new RuntimeException("编码规则[" + serialRule.getRuleCode() + "]配置异常，请联系管理员");
                        }

                        if (partCond.startsWith(STRING_KEY)) {
                            partValue = part;
                        } else if (partCond.startsWith(SEQUENCE_KEY)) {

                            int partLength = Integer.parseInt(partCond.substring(SEQUENCE_KEY.length()));
                            int partValuePre = Integer.parseInt(partValues[index]);

                            // 如果日期改变，序号从1开始
                            if (dateChanged) {
                                partValue = StringUtils.repeat("0" , (partLength - 1)) + "1";
                            } else {

                                int maxValue = Integer.parseInt(StringUtils.repeat("9" , partLength));
                                if (partValuePre >= maxValue) {
                                    throw new RuntimeException(
                                            "数据量超出编码自增长上限值，请扩大编号规则[" + serialRule.getRuleCode() + "]的序列自增长范围，或联系管理员处理");
                                } else {
                                    int partValueNow = partValuePre + 1;
                                    partValue = StringUtils.repeat("0" , partLength - String.valueOf(partValueNow).length())
                                            + partValueNow;
                                }
                            }
                        } else if (partCond.startsWith(DATETIME_KEY)) {
                            SimpleDateFormat sdf = getSdfByPartCond(partCond);
                            partValue = sdf.format(now);
                        }
                        newCode += partValue + (index != partNum - 1 ? delimiter : "");
                    }
                    currentCode = newCode;
                    codes.add(currentCode);
                }
                serialOwner.setCurrentCode(newCode);
                serialOwnerService.saveOrUpdate(serialOwner);
            }
            catch (Exception ex) {
                ex.printStackTrace();
            }
            return codes;
        }
    }

    private static String getSeqKey(String ruleCode, String owner) {
        return MessageFormat.format("{0}{1}:{2}", CACHE_SEQ_PREFIX, owner, ruleCode);
    }

    /**
     * @param serialCodeCond
     * @return SimpleDateFormat
     */
    private static SimpleDateFormat getSdfByPartCond(String serialCodeCond) {
        int dtLength = Integer.parseInt(serialCodeCond.substring(DATETIME_KEY.length()));

        Map<Integer, String> map = new HashMap<Integer, String>() {
            {
                put(14, "yyyyMMddHHmmss");
                put(12, "yyyyMMddHHmm");
                put(10, "yyyyMMddHH");
                put(8, "yyyyMMdd");
                put(6, "yyyyMM");
                put(4, "yyyy");
            }
        };
        return new SimpleDateFormat(map.getOrDefault(dtLength, "yyyyMMddHHmmss"));
    }

}
